package net.amsoft.iservice.isso.util;

import java.util.Iterator;
import java.util.Set;
import net.amsoft.iservice.isso.hibernate.Cookie;
import net.amsoft.iservice.isso.hibernate.IBroker;
import net.amsoft.iservice.isso.hibernate.ISSOObject;
import net.amsoft.iservice.isso.hibernate.Principal;
import net.amsoft.iservice.isso.hibernate.PrincipalMetaData;
import net.amsoft.iservice.isso.hibernate.Response;
import net.amsoft.iservice.isso.hibernate.Synonym;
import net.amsoft.iservice.util.exception.IServiceException;
import net.amsoft.iservice.util.exception.IServiceSystemException;

import org.apache.log4j.Logger;
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.criterion.Expression;

/**
 * public class ISSOHelpers This class provides the main interface between the
 * service layer and the database. Different APIs provided in this class can be
 * used to fetch data objects of hibernate entities which can be used to
 * communicate with database tables. The class ISSOHelpers includes methods for
 * fetching data objects of IBroker, Synonym, Principal, PrincipalMetadata,
 * Response, Cookie etc. It provides general method to fetch a general object
 * which can be used after specific typecast, it also provides special methods
 * to fetch dataobjects with specific attributes. No constructor is provided for
 * this class, all the public static APIs provided in this class can be used
 * without any object to class ISSOHelpers.
 * 
 */
public class ISSOHelpers {
	private static Logger logger = Logger.getLogger(ISSOHelpers.class);

	/**
	 * Used to fetch a generalized object of class cClass, the object can be
	 * used only after a proper typecast.
	 * 
	 * @param cClass
	 *            Class , this method will fetch a data object of this class
	 * @param id
	 * @param oSession
	 *            Hibernate Session that will preserve current requests
	 * @param oTransaction
	 *            Hibernate Transaction
	 * @return Object object of cClass
	 */
	public static Object fetch(Class cClass, Long id, Session oSession,
			Transaction oTransaction) {
		logger.info("fetch() entry");
		ISSOObject oISSOObject = (ISSOObject) oSession.load(cClass, id);
		return (oISSOObject);
	}

	/**
	 * This is a special API to fetch the data object of broker using
	 * sServerName
	 * 
	 * @param sServerName
	 *            Used to fetch the broker data object whose servername matches
	 *            this given string
	 * @param oSession
	 *            Hibernate Session that will preserve current requests
	 * 
	 * @param oTransaction
	 *            Hibernate Transaction for current session
	 * @return IBroker data object of IBroker class
	 * @throws IServiceException
	 */
	public static IBroker fetchIBroker(String sServerName, Session oSession,
			Transaction oTransaction) throws IServiceException {
		logger.info("fetchIBroker() entry with data : ServerName="
				+ sServerName);
		IBroker oIBroker = (IBroker) oSession.createCriteria(IBroker.class)
				.add(Expression.eq("serverName", sServerName)).add(
						Expression.eq("state", Short
								.valueOf(ISSOConstants.STATE_ACTIVE)))
				.uniqueResult();
		if (oIBroker == null) {
			logger.info("fetchIBroker() exit with Exception");
			throw new IServiceException(logger, 2004);
		}
		logger.info("fetchIBroker() exit with Broker=" + oIBroker);
		return (oIBroker);
	}

	/**
	 * This method fetches general data object of Synonym class using its id
	 * 
	 * @param cClass
	 *            Fetch data object of this class
	 * @param id
	 * @param oSession
	 *            Hibernate Session that will preserve current requests
	 * 
	 * @param oTransaction
	 *            Hibernate Transaction for current session
	 * @return Synonym data object of Synonym class
	 */
	public static Synonym fetchSynonym(Class cClass, Long id, Session oSession,
			Transaction oTransaction) {
		logger.info("fetchSynonym() entry");
		Synonym oSynonym = (Synonym) oSession.load(cClass, id);
		return (oSynonym);
	}

	/**
	 * This method returns the Response object where the given sArtifactId
	 * matches the encrypted Artifact Id of the Response
	 * 
	 * @param sArtifactId
	 *            This should match to encrypted Artifact Id of the Response
	 * @param oSession
	 *            Hibernate Session that will preserve current requests
	 * 
	 * @param oTransaction
	 *            Hibernate Transaction for current session
	 * @return Response
	 */
	public static Response fetchResponse(String sArtifactId, Session oSession,
			Transaction oTransaction) {
		logger.info("fetchResponse() entry with data : ArtifactId="
				+ sArtifactId);
		Response oResponse = new Response();
		oResponse = (Response) oSession.createCriteria(Response.class).add(
				Expression.eq("encArtifactId", sArtifactId)).add(
				Expression.eq("state", Short
						.valueOf((short) ISSOConstants.STATE_ACTIVE)))
				.uniqueResult();
		logger.info("fetchSynonym() exit with response=" + oResponse);
		return (oResponse);
	}

	/**
	 * Gets the object of the cookie, the cookie may be a machine cookie or a
	 * session cookie depending on the value of the typeFlag. The cookie is
	 * selected using the given string which is a unique cookie name
	 * 
	 * @param sCookieName
	 *            Name of the cookie in encrypted form
	 * @param typeFlag
	 *            Flag decides whether the cookie is machine cookie or a session
	 *            cookie
	 * @param oSession
	 *            Hibernate Session that will preserve current requests
	 * 
	 * @param oTransaction
	 *            Hibernate Transaction for current session
	 * @return Cookie the data object of the Cookie class
	 * @throws IServiceException
	 */
	public static Cookie fetchCookieByName(String sCookieName, short typeFlag,
			Session oSession, Transaction oTransaction)
			throws IServiceException {
		logger.info("fetchCookieByName() entry with data : CookieName="
				+ sCookieName);
		Cookie oCookie = new Cookie();
		oCookie = (Cookie) oSession.createCriteria(Cookie.class).add(
				Expression.eq("typeFlag", Integer.valueOf(Short
						.toString(typeFlag)))).add(
				Expression.eq("encCookieName", sCookieName)).add(
				Expression.eq("state", Short
						.valueOf((short) ISSOConstants.STATE_ACTIVE)))
				.uniqueResult();
		if (oCookie == null)
			throw new IServiceException(logger, 2033);
		else
			oCookie.update();
		logger.info("fetchCookieByName() : exit");
		return (oCookie);
	}

	/**
	 * Gets the metadata information of the Principal. Metadata may have
	 * information like First Name, Last Name, Email, Encrypted Password etc.
	 * 
	 * @param oPrincipal
	 *            Principal object whose metadata information is to be fetched
	 * @param oSession
	 *            Hibernate Session that will preserve current requests
	 * 
	 * @param oTransaction
	 *            Hibernate Transaction for current session
	 * @return PrincipalMetaData Data object of PrincipalMetaData which has all
	 *         information of the Principal
	 */
	public static PrincipalMetaData fetchMetaData(Principal oPrincipal,
			Session oSession, Transaction oTransaction) {
		logger.info("fetchMetaData() entry");
		PrincipalMetaData oData = new PrincipalMetaData();
		oData = (PrincipalMetaData) oSession.createCriteria(
				PrincipalMetaData.class).add(
				Expression.eq("principal", oPrincipal)).uniqueResult();
		logger.info("fetchMetaData() exit");
		return (oData);
	}

	/**
	 * Gets the Principal object for which this GlobalI-Number is registered.
	 * 
	 * @param sGIN
	 *            Global I-Number is the one registered for the required
	 *            principal
	 * @param oSession
	 *            Hibernate Session that will preserve current requests
	 * 
	 * @param oTransaction
	 *            Hibernate Transaction for current session
	 * @return Principal
	 * @throws IServiceSystemException
	 */
	public static Principal fetchPrincipal(String sGIN, Session oSession,
			Transaction oTransaction) throws IServiceSystemException {
		logger.info("fetchPrincipal() entry with data : GIN=" + sGIN);
		Synonym oGINSyn = (Synonym) oSession.createCriteria(Synonym.class).add(
				Expression.eq("synonym", sGIN)).add(
				Expression.eq("state", Short
						.valueOf((short) ISSOConstants.STATE_ACTIVE)))
				.uniqueResult();
		if (oGINSyn == null) {
			throw new IServiceSystemException(logger, 2006);
		}
		Principal oPrincipal = oGINSyn.getPrincipal();
		logger.info("fetchPrincipal() exit, return Principal");
		return (oPrincipal);
	}

	/**
	 * This is a special API to get the data object of Pricncipal using the
	 * Activation Token. This token is generated at the time of subscription of
	 * a new Global I-Number.
	 * 
	 * @param sActivationToken
	 *            The activation token to be used to fetch principal object
	 * @param oSession
	 *            Hibernate Session that will preserve current requests
	 * 
	 * @param oTransaction
	 *            Hibernate Transaction for current session
	 * @return Principal data object of Principal class
	 * @throws IServiceException
	 */
	public static Principal fetchPrincipalByActivationToken(
			String sActivationToken, Session oSession, Transaction oTransaction)
			throws IServiceException {
		logger
				.info("fetchPrincipalByActivationToken() entry with data : ActivationToken="
						+ sActivationToken);
		Principal oPrincipal = new Principal();
		PrincipalMetaData oData = new PrincipalMetaData();
		oData = (PrincipalMetaData) oSession.createCriteria(
				PrincipalMetaData.class).add(
				Expression.eq("encActivationToken", sActivationToken))
				.uniqueResult();
		if (oData == null) {
			throw new IServiceException(logger, 2015);
		}
		oData.update();
		oPrincipal = oData.getPrincipal();
		logger.info("fetchPrincipalByActivationToken() exit");
		return (oPrincipal);
	}

	/**
	 * Gets the Global I-Number, it uses given id to fetch the Principal which
	 * is further used to choose the Global I-Number.
	 * 
	 * @param Id
	 * @param oSession
	 *            Hibernate Session that will preserve current requests
	 * 
	 * @param oTransaction
	 *            Hibernate Transaction for current session
	 * @return Synonym data object of Synonym class
	 */
	public static Synonym fetchGINo(Long Id, Session oSession,
			Transaction oTransaction) {
		logger.info("fetchGINo() entry with data : Id=" + Id);
		Principal oPrincipal = (Principal) fetch(Principal.class, Id, oSession,
				oTransaction);
		Criteria crit = oSession.createCriteria(Synonym.class);
		crit.add(Expression.eq("principal", oPrincipal));
		crit.add(Expression.isNull("globalINumber"));
		crit.add(Expression.eq("state", Short
				.valueOf((short) ISSOConstants.STATE_ACTIVE)));
		Synonym oSyn = (Synonym) crit.uniqueResult();
		logger.info("fetchGINo() : exit, return GIN");
		return (oSyn);
	}

	/**
	 * Gets the synonym using given string name as query.
	 * 
	 * @param name
	 *            Name of the synonym
	 * @param oSession
	 *            Hibernate Session that will preserve current requests
	 * 
	 * @param oTransaction
	 *            Hibernate Transaction for current session
	 * @return Synonym data object of Synonym class
	 */
	public static Synonym fetchSynonym(String name, Session oSession,
			Transaction oTransaction) {
		logger.info("fetchSynonym() entry with data : Name=" + name);
		Synonym oSyn = new Synonym();
		oSyn = (Synonym) oSession.createCriteria(Synonym.class).add(
				Expression.eq("synonym", name)).add(
				Expression.eq("state", Short
						.valueOf((short) ISSOConstants.STATE_ACTIVE)))
				.uniqueResult();
		logger.info("fetchSynonym() : exit, return Synonym");
		return (oSyn);
	}

	/**
	 * Removes response for the current cookie
	 * 
	 * @param oCookie
	 *            Use this cookie value to remove the response
	 * @param oSession
	 *            Hibernate Session that will preserve current requests
	 * 
	 * @param oTransaction
	 *            Hibernate Transaction for current session
	 * 
	 */
	private static void removeResponse(Cookie oCookie, Session oSession,
			Transaction oTransaction) {
		for (Iterator oIter = oCookie.getResponse().iterator(); oIter.hasNext();) {
			Response oResponse = (Response) oIter.next();
			oResponse.remove();
		}
	}

	/**
	 * It fetches all the synonyms that belong to the given Global I-Number
	 * 
	 * @param sGIN
	 *            Use this Global I-Number to fetch all its synonyms
	 * @param oSession
	 *            Hibernate Session that will preserve current requests
	 * @param oTransaction
	 *            Hibernate Transaction for current session
	 * @return Set set of all the synonyms whose Global I-Number is sGIN
	 */
	public static Set fetchAllSynonyms(String sGIN, Session oSession,
			Transaction oTransaction) {
		logger.info("fetchAllSynonyms() entry with data : GIN=" + sGIN);
		Synonym oGINSyn = (Synonym) oSession.createCriteria(Synonym.class).add(
				Expression.eq("synonym", sGIN)).add(
				Expression.eq("state", Short
						.valueOf((short) ISSOConstants.STATE_ACTIVE)))
				.uniqueResult();
		Principal oPrincipal = oGINSyn.getPrincipal();
		logger.info("fetchAllSynonyms() : exit");
		return oPrincipal.getSynonyms();
	}

	/**
	 * It creates cookie from the given encrypted session token and deletes its
	 * response if it exists otherwise throws exception
	 * 
	 * @param sSessionToken
	 *            Name of the Cookie in the Encrypted form
	 * @param oSession
	 *            Hibernate Session that will preserve current requests
	 * @param oTransaction
	 *            Hibernate Transaction for current session
	 * 
	 * @throws IServiceException
	 */
	public static void deleteSession(String sSessionToken, Session oSession,
			Transaction oTransaction) throws IServiceException {
		logger.info("deleteSession() entry with data : SessionId="
				+ sSessionToken);
		Cookie oCookie = new Cookie();
		oCookie = (Cookie) oSession.createCriteria(Cookie.class).add(
				Expression.eq("typeFlag", Integer
						.valueOf(ISSOConstants.SESSION))).add(
				Expression.eq("encCookieName", sSessionToken)).uniqueResult();
		if (oCookie == null) {
			throw new IServiceException(logger, 2033);
		}
		removeResponse(oCookie, oSession, oTransaction);
		oCookie.remove();
		oSession.delete(oCookie);
		logger.info("deleteSession() exit after cookie remove");
	}
}
